package com.atguigu.daijia.map.service.impl;

import com.atguigu.daijia.common.constant.RedisConstant;
import com.atguigu.daijia.common.constant.SystemConstant;
import com.atguigu.daijia.common.util.LocationUtil;
import com.atguigu.daijia.driver.client.DriverInfoFeignClient;
import com.atguigu.daijia.map.repository.OrderServiceLocationRepository;
import com.atguigu.daijia.map.service.LocationService;
import com.atguigu.daijia.model.entity.driver.DriverSet;
import com.atguigu.daijia.model.entity.map.OrderServiceLocation;
import com.atguigu.daijia.model.form.map.OrderServiceLocationForm;
import com.atguigu.daijia.model.form.map.SearchNearByDriverForm;
import com.atguigu.daijia.model.form.map.UpdateDriverLocationForm;
import com.atguigu.daijia.model.form.map.UpdateOrderLocationForm;
import com.atguigu.daijia.model.vo.map.NearByDriverVo;
import com.atguigu.daijia.model.vo.map.OrderLocationVo;
import com.atguigu.daijia.model.vo.map.OrderServiceLastLocationVo;
import lombok.extern.slf4j.Slf4j;
import org.bson.types.ObjectId;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.Sort;
import org.springframework.data.geo.*;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.redis.connection.RedisGeoCommands;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

@Slf4j
@Service
@SuppressWarnings({"unchecked", "rawtypes"})
public class LocationServiceImpl implements LocationService {

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private DriverInfoFeignClient driverInfoFeignClient;

    //mongodb
    @Autowired
    private OrderServiceLocationRepository orderServiceLocationRepository;

    @Autowired
    private MongoTemplate mongoTemplate;

    /*
     * 更新司机位置信息
     * */
    @Override
    public Boolean updateDriverLocation(UpdateDriverLocationForm updateDriverLocationForm) {
        //把司机的位置信息添加到redis的geo中
        Point point = new Point(updateDriverLocationForm.getLongitude().doubleValue()
                               ,updateDriverLocationForm.getLatitude().doubleValue());//位置信息，经纬度
        //将位置添加到redis中
        redisTemplate.opsForGeo().add(RedisConstant.DRIVER_GEO_LOCATION
                                     ,point
                                     ,updateDriverLocationForm.getDriverId());
        return true;
    }

    /*
     * 删除司机位置信息
     * */
    @Override
    public Boolean removeDriverLocation(Long driverId) {
        redisTemplate.opsForGeo().remove(RedisConstant.DRIVER_GEO_LOCATION, driverId.toString());
        return true;
    }

    /*
     * 搜索适合接单的司机司机
     * */
    @Override
    public List<NearByDriverVo> searchNearByDriver(SearchNearByDriverForm searchNearByDriverForm) {
        //搜索用户经纬度位置5公里以内的司机 操作redis中的geo
        //Circle
        Point point = new Point(searchNearByDriverForm.getLongitude().doubleValue()
                ,searchNearByDriverForm.getLatitude().doubleValue());//位置信息，经纬度
        Distance distance = new Distance(SystemConstant.NEARBY_DRIVER_RADIUS, RedisGeoCommands.DistanceUnit.KILOMETERS);
        Circle circle = new Circle(point,distance);
        //geo，设置返回结果里面包含的内容
        RedisGeoCommands.GeoRadiusCommandArgs args = RedisGeoCommands
                .GeoRadiusCommandArgs
                .newGeoRadiusArgs()
                .includeDistance()//包含距离
                .includeCoordinates()//包含坐标
                .sortAscending();//升序

        //查询到的内容
        GeoResults<RedisGeoCommands.GeoLocation<String>> results = redisTemplate.opsForGeo().radius(RedisConstant.DRIVER_GEO_LOCATION, circle, args);

        //查询司机信息
        List<GeoResult<RedisGeoCommands.GeoLocation<String>>> content = results.getContent();
        //封装司机信息到NearByDriverVo，根据司机的个性化信息判断
        //new返回值
        List<NearByDriverVo> list = new ArrayList<>();
        if(!content.isEmpty()){
            //不为空才遍历
            for (GeoResult<RedisGeoCommands.GeoLocation<String>> geoLocationGeoResult : content) {
                //获取司机id
                Long driverId = Long.valueOf(geoLocationGeoResult.getContent().getName());
                //获取司机位置
                Point driverPoint = geoLocationGeoResult.getContent().getPoint();
                //判断该订单是否满足司机个性化要求 远程调用
                DriverSet driverSet = driverInfoFeignClient.getDriverSet(driverId).getData();
                //司机的订单里程限制
                //如果司机里程限制为0：说明可以直接通过
                //司机的里程减该订单里程必须小于0
                if(driverSet.getOrderDistance().doubleValue() != 0 &&
                        driverSet.getOrderDistance().subtract(searchNearByDriverForm.getMileageDistance()).doubleValue() < 0){
                    continue;
                }
                //判断司机接单里程
                BigDecimal acceptDistance = driverSet.getAcceptDistance();
                if(acceptDistance.doubleValue() != 0 &&
                        acceptDistance.subtract(BigDecimal.valueOf(geoLocationGeoResult.getDistance().getValue())).doubleValue()<0){
                    continue;
                }
                NearByDriverVo nearByDriverVo = new NearByDriverVo();
                nearByDriverVo.setDriverId(driverId);
                nearByDriverVo.setDistance(driverSet.getOrderDistance());
                list.add(nearByDriverVo);
            }
        }
        //返回
        return list;
    }

    /*
     * 司机赶往代驾起始点：更新订单地址到缓存
     * */
    @Override
    public Boolean updateOrderLocationToCache(UpdateOrderLocationForm updateOrderLocationForm) {
        OrderLocationVo orderLocationVo = new OrderLocationVo();
        orderLocationVo.setLongitude(updateOrderLocationForm.getLongitude());
        orderLocationVo.setLatitude(updateOrderLocationForm.getLatitude());
        //将司机经纬度添加到redis
        redisTemplate.opsForValue().set(RedisConstant.UPDATE_ORDER_LOCATION + updateOrderLocationForm.getOrderId()
        ,orderLocationVo);
        return true;
    }

    /*
     * 司机赶往代驾起始点：获取订单经纬度位置
     * */
    @Override
    public OrderLocationVo getCacheOrderLocation(Long orderId) {
        //拿redis中保存的地址
        OrderLocationVo orderLocationVo = (OrderLocationVo) redisTemplate.opsForValue().get(RedisConstant.UPDATE_ORDER_LOCATION + orderId);
        return orderLocationVo;
    }

    /*
     * 批量保存代驾服务订单位置 存储到mongodb中
     * */
    @Override
    public Boolean saveOrderServiceLocation(List<OrderServiceLocationForm> orderServiceLocationFormList) {
        //遍历集合
        for (OrderServiceLocationForm orderServiceLocationForm : orderServiceLocationFormList) {
            OrderServiceLocation orderServiceLocation = new OrderServiceLocation();
            BeanUtils.copyProperties(orderServiceLocationForm, orderServiceLocation);//对象拷贝
            orderServiceLocation.setId(ObjectId.get().toString());
            orderServiceLocation.setCreateTime(new Date());
            orderServiceLocationRepository.save(orderServiceLocation);//添加到mongodb中
        }
        return true;
    }

    /*
     * 代驾服务：获取订单服务最后一个位置信息
     * */
    @Override
    public OrderServiceLastLocationVo getOrderServiceLastLocation(Long orderId) {
        //查询mongodb 条件：orderid，降序排序的第一个位置信息
        Query query = new Query();
        query.addCriteria(Criteria.where("orderId").is(orderId));//查询
        query.with(Sort.by(Sort.Order.desc("createTime")));//排序
        query.limit(1);//取一条数据
        //拿到这个数据
        OrderServiceLocation orderServiceLocation = mongoTemplate.findOne(query, OrderServiceLocation.class);
        //封装到返回值vo中
        OrderServiceLastLocationVo orderServiceLastLocationVo = new OrderServiceLastLocationVo();
        //对象拷贝
        BeanUtils.copyProperties(orderServiceLocation, orderServiceLastLocationVo);
        return orderServiceLastLocationVo;
    }

    /*
     * 代驾服务：计算订单实际里程
     * */
    @Override
    public BigDecimal calculateOrderRealDistance(Long orderId) {
        //1.根据订单id从mongodb中获取保存的订单位置信息，根据创建时间升序排序
        OrderServiceLocation orderServiceLocation = new OrderServiceLocation();
        orderServiceLocation.setOrderId(orderId);
        Example<OrderServiceLocation> example = Example.of(orderServiceLocation);
        Sort sort = Sort.by(Sort.Direction.ASC,"createTime");
        List<OrderServiceLocation> list = orderServiceLocationRepository.findAll(example, sort);
        //2.第一步查询返回所有位置信息的list集合，把list遍历得到每个位置，计算两个临界位置的距离，把所有距离相加
        BigDecimal allDistance = BigDecimal.ZERO;//最终要返回的值
        if (!list.isEmpty()) {
            for (int i = 0; i < list.size()-1; i++) {
                OrderServiceLocation one = list.get(i);
                OrderServiceLocation two = list.get(i + 1);
                double distance = LocationUtil.getDistance(one.getLatitude().doubleValue(),
                        one.getLongitude().doubleValue(),
                        two.getLatitude().doubleValue(),
                        two.getLongitude().doubleValue());
                allDistance.add(BigDecimal.valueOf(distance));

            }
        }
        //3.返回最终计算的实际距离
        return allDistance;
    }
}
